﻿using System;
using System.Configuration;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using MvcContrib.Unity;
using MvcContrib.Interfaces;
using JqueryMvc.Mvc;

namespace JqueryMvc
{
    abstract public class JqueryMvcApplication : HttpApplication, IUnityContainerAccessor
    {
        //Routes, ViewEngines and the container-init are all statics and should thus be
        //done in the Application_Start... this is the only method wich is guaranteed to be
        //called upon only one
        public virtual void Application_Start()
        {
            lock (typeof(JqueryMvcApplication))
            {
                InitializeContainer();
                ConfigureContainer();
                RegisterRoutes(RouteTable.Routes);
                SetupViewEngines();
            }
        }

        virtual public void SetupViewEngines()
        {
            ViewEngines.Engines.Add(new DefaultViewViewEngine());
        }

        public virtual void ConfigureContainer()
        {
            UnityConfigurationSection section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
            if (section != null)
                section.Containers.Default.Configure(Container);
        }

		virtual public void RegisterRoutes(RouteCollection routes)
		{
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            string prefix = null;
            if (System.IO.File.Exists(this.Server.MapPath("~/default.aspx")))
                prefix = ".aspx";
            
            //if you live on a server that doesn't need the 
            //default.aspx... you can delete the default.aspx,
            //and routing will be skipped for existing files...
            if (System.IO.File.Exists(this.Server.MapPath("~/default.aspx")))
            {
                routes.IgnoreRoute("{*rest}", new { rest = "^Content\\/.*" });
                routes.RouteExistingFiles = true;
                routes.MapRoute(
                    "Default.aspx",         
                    "default.aspx",         
                    new
                    {
                        controller = "Home",
                        action = "Index",
                        id = ""
                    }                       
                );
                routes.MapRoute(
                    "Default",                     
                    "{controller}.aspx/{action}/{id}",  
                    new { 
                        controller = "Home", 
                        action = "Index", 
                        id = "" },                  
                    new { controller = "[^\\.]*", action = "[^\\.]*" }
                );
            }
            else
            {
                routes.MapRoute(
                    "Default",                                               
                    "{controller}/{action}/{id}",                            
                    new { controller = "Home", action = "Index", id = "" },  
                    new { controller = "[^\\.]*", action = "[^\\.]*" }       
                );
            }
        }
		
		#region IoC
        virtual public void InitializeContainer()
        {
            lock (containerLock)
            {
                container = new UnityContainer();


                container
                    .RegisterInstance<IUnityContainer>(container)
                    .RegisterType<IDependencyResolver, UnityDependencyResolver>();

                ControllerBuilder.Current.SetControllerFactory(typeof(UnityControllerFactory));
                MvcContrib.Services.DependencyResolver.InitializeWith(container.Resolve<IDependencyResolver>());
            }
        }

        static object containerLock = new object();
        static UnityContainer container;
        public UnityContainer Container
        {
            get
            {
                if (container == null)
                    InitializeContainer();
                return container;
            }
        }
        #endregion

        #region IoC Helper Methods
        virtual protected void AddFromNamespace(Type typeInNs, bool useInterfaces, bool recursive, bool asSingleton)
        {
            Type[] types = typeInNs.Assembly.GetTypes();
            foreach (Type type in types)
            {
                if (!type.IsInterface && !type.IsAbstract && !type.IsEnum && !type.IsNotPublic && !type.IsPointer && 
                    !type.IsPrimitive && !type.IsValueType)
                {
                    if ((recursive && type.Namespace != null && type.Namespace.StartsWith(typeInNs.Namespace))
                        ||
                        (type.Namespace == typeInNs.Namespace))
                    {
                        if (asSingleton)
                        {
                            container.RegisterInstance(container.Resolve(type));
                        }
                        AddType(type, useInterfaces);
                    }
                }
            }
        }

        virtual protected void AddType(Type type, bool useInterfaces)
        {
            if (type.IsAbstract || !type.IsClass)
                return;
            if (useInterfaces)
            {
                Type serviceType = GetServiceType(type);
                if (serviceType == null)
                    return;
                AddService(serviceType, type);
            }
            else
                AddService(type);
        }

        virtual protected string GetTypeKey(Type type)
        {
            return type.FullName;
        }

        virtual protected Type GetServiceType(Type type)
        {
            Type[] interfaces = type.FindInterfaces(new TypeFilter(delegate(Type t, object o) { return true; }), null);
            foreach (Type iface in interfaces)
            {
                return iface;
            }
            return null;
        }

        virtual protected void AddService(Type serviceType, Type type)
        {
            Container.RegisterType(serviceType, type);
        }

        virtual protected void AddService(Type type)
        {
            Container.RegisterType(type, type);
        }
        #endregion

        #region IContainerAccessor Members

        IUnityContainer IUnityContainerAccessor.Container
        {
            get { return Container; }
        }

        #endregion
    }
}
